//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;

namespace Microsoft.Research.eResearch
{
    /// <summary>
    /// Direction of the parameter being sent to the Connection
    /// </summary>
    public enum Direction
    {
        /// <summary>
        /// Input parameter. Caller provides the value.
        /// </summary>
        In,

        /// <summary>
        /// Output parameter. Caller expects the value on completion of operation.
        /// </summary>
        Out,

        /// <summary>
        /// Reference parameter. Caller provides a value and expects a potential update
        /// on the value on completion of operation.
        /// </summary>
        Ref,
    }

    /// <summary>
    /// Basic types supported by the backend.
    /// </summary>
    public enum Type
    {
        /// <summary>
        /// No type.
        /// </summary>
        Void,

        /// <summary>
        /// Unknown type.
        /// </summary>
        Unknown,

        /// <summary>
        /// String
        /// </summary>
        String,

        /// <summary>
        /// Date + Time
        /// </summary>
        DateTime,

        /// <summary>
        /// Integer
        /// </summary>
        Integer,

        /// <summary>
        /// Boolean
        /// </summary>
        Boolean,

        /// <summary>
        /// Double precision number
        /// </summary>
        Double,

        /// <summary>
        /// Object
        /// </summary>
        Object,
    }

    /// <summary>
    /// Data abstraction of a "cell" (or "column") in a data storage. Each parameter
    /// represents named data coming in, out or by reference from/to the data storage.
    /// </summary>
    public class Parameter
    {
        /// <summary>
        /// Direction of the parameter
        /// </summary>
        public enum ParamDirection
        {
            /// <summary>
            /// Input parameter. Data flows from the caller to the data storage
            /// </summary>
            In,

            /// <summary>
            /// Output parameter. Data flows from the data storage to the caller.
            /// This is setup by the caller and should be honored by the data store.
            /// </summary>
            Out,

            /// <summary>
            /// Result parameter. Represents a value that is the result of an operation.
            /// Right now it is a synonym of <c>ParamDirection.Out</c> but the context used
            /// is different, such as when creating a new record the ID of the new record
            /// is treated as a result.
            /// </summary>
            Result,

            /// <summary>
            /// Represents a pipeline between the data storage and the caller. Used to retrieve
            /// multiple objects in a single call.
            /// </summary>
            Pipeline,

            /// <summary>
            /// Never used.
            /// </summary>
            Unknown,
        }

        /// <summary>
        /// Base type of the object being sent to/from the data storage to/from the caller.
        /// </summary>
        public enum ParamType
        {
            /// <summary>
            /// Boolean
            /// </summary>
            Boolean,

            /// <summary>
            /// Integer
            /// </summary>
            Int,

            /// <summary>
            /// Guid
            /// </summary>
            Guid,

            /// <summary>
            /// String
            /// </summary>
            String,

            /// <summary>
            /// DateTime
            /// </summary>
            DateTime,

            /// <summary>
            /// Text
            /// </summary>
            Text,

            /// <summary>
            /// Color
            /// </summary>
            Color,

            /// <summary>
            /// Image
            /// </summary>
            Image,

            /// <summary>
            /// Pipeline
            /// </summary>
            Pipeline,
        }

        /// <summary>
        /// Name of the data
        /// </summary>
        public string Name;

        /// <summary>
        /// Base type of data
        /// </summary>
        public ParamType Type;

        /// <summary>
        /// Direction of the parameter
        /// </summary>
        public ParamDirection Direction;

        /// <summary>
        /// Value of the data
        /// </summary>
        public object Value;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="name"></param>
        /// <param name="type"></param>
        /// <param name="dir"></param>
        /// <param name="value"></param>
        public Parameter(string name, ParamType type, ParamDirection dir, object value)
        {
            this.Name = name;
            this.Type = type;
            this.Direction = dir;
            this.Value = value;
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="name"></param>
        /// <param name="type"></param>
        /// <param name="dir"></param>
        public Parameter(string name, ParamType type, ParamDirection dir)
            : this(name, type, dir, null)
        {
        }

        /// <skipdoc />
        public override string ToString()
        {
            return string.Format("{0} ({1}) = '{2}' [{3}]", this.Name, this.Type.ToString(), 
                (this.Value == null ? "<null>" : this.Value.ToString()),
                this.Direction.ToString());
        }
    }

    /// <summary>
    /// Basic interface to allow objects be created from data
    /// </summary>
    public interface IPipelineParameter
    {
        /// <summary>
        /// Creation function that knows how to create the object from data
        /// </summary>
        Connection.ObjectCreator Creator { get; }

        /// <summary>
        /// Object result of the call to the creation function
        /// </summary>
        object Value { get; set; }
    }

    /// <summary>
    /// Basic class that allows an object be created from data in the backend.
    /// Used by the simple object interfaces to abstract the object details from the
    /// interfaces and keep them generic enough to avoid reimplementation.
    /// </summary>
    /// <typeparam name="T">Object type being created via the pipeline</typeparam>
    public class PipelineParameter<T> : Parameter, IPipelineParameter
    {
        private Connection.ObjectCreator creator;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="creator">Creation function (factory)</param>
        public PipelineParameter(Connection.ObjectCreator creator)
            : base("*PIPELINE*", ParamType.Pipeline, ParamDirection.Pipeline)
        {
            this.creator = creator;
        }

        /// <summary>
        /// Creation function that will build object from data
        /// </summary>
        public Connection.ObjectCreator Creator
        {
            get { return this.creator; }
        }

        /// <summary>
        /// Returns a strong typed version of the object, constructed from the 
        /// data coming from the pipeline.
        /// </summary>
        public new T Value
        {
            get { return (T)base.Value; }
        }

        #region IPipelineParameter Members

        object IPipelineParameter.Value
        {
            get
            {
                return base.Value;
            }
            set
            {
                base.Value = value;
            }
        }

        #endregion
    }
}
